#pragma once

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include "../base/types.h"


OPEN_JLIB_NS


struct RSA {


  inline RSA () noexcept : rsa_pri(nullptr), rsa_pub(nullptr), blk_pri(0), blk_pub(0), max_pri(0), max_pub(0) {
  }


  inline ~RSA () noexcept {
    if (this->rsa_pri)
      RSA_free(this->rsa_pri);
    if (this->rsa_pub)
      RSA_free(this->rsa_pub);
  }



  inline uwl private_encrypt_size (uwl src_size) noexcept {
    if (!this->rsa_pri)
      return 0;
    return (src_size + this->max_pri - 1) / this->max_pri * this->blk_pri;
  }

  inline uwl private_decrypt_size (uwl src_size) noexcept {
    return src_size / this->blk_pri * this->max_pri;
  }

  inline uwl public_encrypt_size (uwl src_size) noexcept {
    if (!this->rsa_pub)
      return 0;
    return (src_size + this->max_pub - 1) / this->max_pub * this->blk_pub;
  }

  inline uwl public_decrypt_size (uwl src_size) noexcept {
    return src_size / this->blk_pub * this->max_pub;
  }



  bool setup_private (const c8 *filename) noexcept;

  bool setup_private (const void *key, uwl size) noexcept;

  bool setup_public (const c8 *filename) noexcept;

  bool setup_public (const void *key, uwl size) noexcept;



  inline void reset_private () noexcept {
    if (this->rsa_pri) {
      RSA_free(this->rsa_pri);
      this->rsa_pri = nullptr;
    }
  }

  inline void reset_public () noexcept {
    if (this->rsa_pub) {
      RSA_free(this->rsa_pub);
      this->rsa_pub = nullptr;
    }
  }



  swl private_encrypt (void *dst, const void *src, uwl src_size) noexcept;

  swl private_decrypt (void *dst, const void *src, uwl src_size) noexcept;

  swl public_encrypt (void *dst, const void *src, uwl src_size) noexcept;

  swl public_decrypt (void *dst, const void *src, uwl src_size) noexcept;


private:
  ::RSA *rsa_pri;
  ::RSA *rsa_pub;
  uwl blk_pri;
  uwl blk_pub;
  uwl max_pri;
  uwl max_pub;
};


CLOSE_JLIB_NS